﻿using System;
using System.Collections.Generic;

namespace Core
{
    /// <summary>
    /// 定时器
    /// </summary>
    public class TimerSystem : Core.Component, IAwake, IUpdate
    {
        private readonly Queue<TickItem> tickQueue = new();
        private readonly Queue<DelayItem> delayQueue = new();
        private readonly Queue<FrameItem> frameQueue = new();
        private readonly Queue<SecondTickItem> secondTickQueue = new();

        private readonly HashSet<long> keySet = new();

        public void Awake()
        {
            tickQueue.Clear();
            delayQueue.Clear();
            frameQueue.Clear();
            secondTickQueue.Clear();
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            foreach (var item in tickQueue)
            {
                item.Dispose();
            }
            tickQueue.Clear();

            foreach (var item in delayQueue)
            {
                item.Dispose();
            }
            delayQueue.Clear();

            foreach (var item in frameQueue)
            {
                item.Dispose();
            }
            frameQueue.Clear();

            foreach (var item in secondTickQueue)
            {
                item.Dispose();
            }
            secondTickQueue.Clear();
        }

        public void Update()
        {
            UpdateTick();
            UpdateDelay();
            UpdateFrame();
            UpdateSecondTick();
        }

        private void UpdateTick()
        {
            for (int i = 0; i < tickQueue.Count; i++)
            {
                var item = tickQueue.Dequeue();
                if (keySet.Contains(item.InstanceId))
                {
                    //log.err($"======= UpdateSystem.Dispose id={item.InstanceId}");
                    item.Dispose();
                    keySet.Remove(item.InstanceId);
                    continue;
                }
                item.Update();
                //if (stop_list_backup.Contains(item.InstanceId))
                //{
                //    log.err("3333333");
                //}
                tickQueue.Enqueue(item);
            }
            //stop_list.Clear();

        }

        private void UpdateDelay()
        {
            for (int i = 0; i < delayQueue.Count; i++)
            {
                var item = delayQueue.Dequeue();
                if (keySet.Contains(item.InstanceId))
                {
                    item.Dispose();
                    keySet.Remove(item.InstanceId);
                    continue;
                }
                item.Update();
                if (item.IsComplete)
                {
                    item.Dispose();
                    keySet.Remove(item.InstanceId);
                    continue;
                }
                delayQueue.Enqueue(item);
            }
        }

        private void UpdateFrame()
        {
            for (int i = 0; i < frameQueue.Count; i++)
            {
                var item = frameQueue.Dequeue();
                if (keySet.Contains(item.InstanceId))
                {
                    item.Dispose();
                    keySet.Remove(item.InstanceId);
                    continue;
                }
                item.Update();
                frameQueue.Enqueue(item);
            }
        }

        private void UpdateSecondTick()
        {
            for (int i = 0; i < secondTickQueue.Count; i++)
            {
                var item = secondTickQueue.Dequeue();
                if (keySet.Contains(item.InstanceId))
                {
                    item.Dispose();
                    keySet.Remove(item.InstanceId);
                    continue;
                }
                item.Update();
                secondTickQueue.Enqueue(item);
            }
        }

        /// <summary>
        /// 开始定时器
        /// </summary>
        /// <param name="func">定时器回调函数</param>
        /// <param name="interval">定时器时间间隔</param>
        /// <param name="disperse">是否打散,将大量同一时间段内触发的定时器，随机分散到[0,interval)内的某一帧执行</param>
        /// <param name="immediate">调用时是否立即回调</param>
        /// <returns></returns>
        public long Start(Action<float> func, float interval, bool disperse = true, bool immediate = true)
        {
            if (func == null) return 0;

            var item = G.Factory.Create<TickItem>();
            item.Interval = interval;
            item.Breakup = disperse;
            item.Immediate = immediate;
            item.Func = func;
            item.Start();

            tickQueue.Enqueue(item);
            //log.err($"======= UpdateSystem.Start id={item.InstanceId}");
            return item.InstanceId;
        }


        public long Tick(Action<int> func, int second, bool disperse = true, bool unscaled = false)
        {
            if (func == null) return 0;

            var item = G.Factory.Create<SecondTickItem>();
            item.Interval = second;
            item.Breakup = disperse;
            item.UnscaledTimeMode = unscaled;
            item.Func = func;
            item.Start();

            secondTickQueue.Enqueue(item);
            return item.InstanceId;
        }


        public long Delay(Action func, float delay_time)
        {
            if (func == null) return 0;

            var item = G.Factory.Create<DelayItem>();
            item.Interval = delay_time;
            item.Func = func;
            item.Start();
            delayQueue.Enqueue(item);

            return item.InstanceId;
        }

        public long Frame(Action<int> func, int interval = 1)
        {
            if (func == null) return 0;

            var item = G.Factory.Create<FrameItem>();
            item.Interval = interval;
            //item.Immediate = immediate;
            item.Func = func;
            item.Start();

            frameQueue.Enqueue(item);
            return item.InstanceId;
        }

        public void Stop(long id)
        {
            if (id <= 0) return;

            keySet.Add(id);
            //stop_list_backup.Add(id);
            //log.err($"======= UpdateSystem.Stop id={id}");
        }
    }
}
